<template>
  <t-chat class="chat-container" @clear="handleClear">
    <template v-for="(item, index) in chatList" :key="index">
      <t-chat-item
        :avatar="item.avatar"
        :name="item.name"
        :role="item.role"
        :datetime="item.datetime"
        :variant="item.variant"
        :text-loading="index === 0 && isLoading"
        :reasoning="{
          expandIconPlacement: 'right',
          collapsePanelProps: {
            header: renderHeader(index === 0 && isStreamLoad, item),
            content: renderReasoningContent(item.reasoning),
          },
        }"
      >
        <template #content>
          <t-space
            :break-line="true"
            :size="16"
            v-if="item.images && item.images?.length > 0"
          >
            <t-space
              v-for="(base64Image, index) in item.images"
              :key="index"
              direction="vertical"
              class="image-container"
            >
              <t-image
                :src="base64Image"
                :style="{ width: '100px', height: '100px' }"
                fit="scale-down"
              />
            </t-space>
          </t-space>
          <t-chat-content :content="item.content"> </t-chat-content>
          <t-chat-action
            v-if="
              index === 0 &&
              !isLoading &&
              !isStreamLoad &&
              item.role === 'assistant'
            "
            :isGood="feedback === 'good'"
            :isBad="feedback === 'bad'"
            :content="item.content"
            @operation="handleActionOperation"
          />
        </template>
      </t-chat-item>
    </template>
    <template #footer>
      <t-chat-sender
        class="chat-sender"
        :stop-disabled="isStreamLoad"
        :textarea-props="{
          placeholder: '请输入消息...',
        }"
        @send="handleSend"
        @stop="handleStop"
      >
        <template #prefix>
          <div class="model-select">
            <t-select
              name="model-select"
              v-model="selectedModel"
              :options="modelList"
              value-type="object"
              @change="handleModelChange"
            >
            </t-select>
            <t-button
              v-if="selectedModel.value.startsWith('deepseek')"
              class="check-box"
              :class="{ 'is-active': isDeepSeekChecked }"
              variant="text"
              @click="deepseekCheckClick"
            >
              <t-icon name="system-sum" />
              <span>深度思考</span>
            </t-button>
            <label
              v-if="
                selectedModel.value.startsWith('llava') ||
                selectedModel.value.startsWith('llama')
              "
              class="check-box"
            >
              <input
                type="file"
                accept="image/*"
                @change="handleImgChange"
                style="display: none"
              />
              <t-icon name="image-add" />
              <span>添加图片</span>
            </label>
            <t-badge
              :count="base64Images.length"
              v-if="
                selectedModel.value.startsWith('llava') ||
                selectedModel.value.startsWith('llama')
              "
            >
              <t-button class="check-box images-btn" @click="visible = true">
                <t-icon name="image" /> <span></span>
              </t-button>
            </t-badge>
          </div>
        </template>
      </t-chat-sender>
    </template>
  </t-chat>
  <t-dialog
    v-model:visible="visible"
    :footer="false"
  >
    <t-space :break-line="true" :size="16">
      <t-space
        v-for="(base64Image, index) in base64Images"
        :key="index"
        direction="vertical"
        class="image-container"
      >
        <div class="image-wrapper">
          <t-image
            :src="base64Image"
            :style="{ width: '100px', height: '100px' }"
            fit="scale-down"
          />
          <t-button
            class="delete-btn"
            variant="plain"
            shape="circle"
            @click="removeImage(index)"
          >
            <t-icon name="delete-1" />
          </t-button>
        </div>
      </t-space>
    </t-space>
  </t-dialog>
</template>
<script setup lang="jsx">
//lang="jsx"
import { ref } from "vue";
import { useChatByOllama } from "@/utils/useChatByOllama";
import { CheckCircleIcon } from "tdesign-icons-vue-next";

// 是否流在加载中
const isStreamLoad = ref(false);
// 加载状态
const isLoading = ref(false);
// AbortController对象用于取消SSE
const abortController = ref(null);

const feedback = ref(null);

const chatList = ref([]);

const selectedModel = ref({
  label: "deepseek-r1:8b",
  value: "deepseek-r1:8b",
});

const modelList = [
  {
    label: "deepseek-r1:8b",
    value: "deepseek-r1:8b",
  },
  {
    label: "deepseek-r1:14b",
    value: "deepseek-r1:14b",
  },
  {
    label: "deepseek-r1:32b",
    value: "deepseek-r1:32b",
  },
  {
    label: "qwen2.5:7b",
    value: "qwen2.5:7b",
  },
  {
    label: "llava:7b",
    value: "llava:7b",
  },
  {
    label: "llama3.2-vision:latest",
    value: "llama3.2-vision:latest",
  },
];

const handleModelChange = (value, context) => {
  isDeepSeekChecked.value = false;
  const modelChangeChatItem = {
    content: `由模型 ${value.label} 提供数据支持`,
    role: "model-change",
    variant: "base",
  };
  chatList.value.unshift(modelChangeChatItem);
};

function handleSend(value, context) {
  if (isStreamLoad.value) {
    return;
  }
  if (isLoading.value) {
    return;
  }
  feedback.value = null;
  const model = selectedModel.value.value;

  const userChatItem = {
    avatar: "https://tdesign.gtimg.com/site/avatar.jpg",
    name: "季舟",
    datetime: new Date().toISOString(),
    content: value,
    role: "user",
    variant: "text",
  };
  if (model.startsWith("llava") || model.startsWith("llama")) {
    userChatItem["images"] = [];
    userChatItem.images.push(...base64Images.value);
    // base64Images.value;
    console.log(userChatItem);
    base64Images.value = [];
  }
  chatList.value.unshift(userChatItem);

  const assistantChatItem = {
    avatar: "https://tdesign.gtimg.com/site/chat-avatar.png",
    name: "AI Assistant [" + model + "]",
    datetime: new Date().toISOString(),
    content: "",
    role: "assistant",
    variant: "text",
  };
  chatList.value.unshift(assistantChatItem);
  chatWithOllama(userChatItem, model);
}

function chatWithOllama(userChatItem, model) {
  isStreamLoad.value = true;
  isLoading.value = true;

  const lastItem = chatList.value[0];

  const llmModel = {
    model: model,
    deepseek: isDeepSeekChecked.value,
  };

  let isDeepSeekThinking = false;
  const { controller } = useChatByOllama(userChatItem, llmModel, {
    onMessage(chunk) {
      isLoading.value = false;
      const llmModelName = selectedModel.value.value;

      if (llmModelName.startsWith("deepseek") || llmModelName.startsWith("qwen")) {
        const { done, message, total_duration: duration } = chunk;
        if (done) {
          lastItem["duration"] = duration;
        } else {
          const { content } = message;
          if (content === "\u003cthink\u003e") {
            isDeepSeekThinking = true;
            lastItem["reasoning"] = "";
          } else if (content === "\u003c/think\u003e") {
            isDeepSeekThinking = false;
          } else {
            if (isDeepSeekThinking) {
              lastItem.reasoning += content;
            } else {
              lastItem.content += content;
            }
          }
        }
      } else if (
        selectedModel.value.value.startsWith("llava") ||
        selectedModel.value.value.startsWith("llama")
      ) {
        const { done, response, total_duration: duration } = chunk;
        if (done) {
          lastItem["duration"] = duration;
        } else {
          lastItem.content += response;
        }
      } else {
      }
    },
    onError(error) {
      lastItem.content += error;
      lastItem.role = "error";
      isLoading.value = false;
      isStreamLoad.value = false;
    },
    onComplete() {
      isLoading.value = false;
      isStreamLoad.value = false;
    },
  });
  abortController.value = controller;
}

function handleStop() {
  abortController.value?.abort();
  isStreamLoad.value = false;
  isLoading.value = false;
}

function handleClear() {
  chatList.value = [];
}

function handleActionOperation(value, context) {
  if (value === "good" || value === "bad") {
    feedback.value = value;
  } else if (value === "replay") {
    if (chatList.value.length > 1) {
      chatList.value[0].content = "";
      chatList.value[0].datetime = new Date().toISOString();
      chatList.value[0].role = "assistant";
      const model = selectedModel.value.value;
      chatWithOllama(chatList.value[1], model);
    }
  }
}

const renderHeader = (flag, item) => {
  if (flag) {
    return <t-chat-loading text="思考中..." />;
  }
  const totalDuration = parseFloat(
    parseFloat(item.duration) / 1000000000
  ).toFixed(1);
  const endText = item.duration
    ? `已深度思考(用时${totalDuration}秒)`
    : "已深度思考";
  return (
    <div style="display:flex;align-items:center">
      <CheckCircleIcon
        style={{
          color: "var(--td-success-color-5)",
          fontSize: "20px",
          marginRight: "8px",
        }}
      />
      <span>{endText}</span>
    </div>
  );
};

const renderReasoningContent = (reasoningContent) => (
  <t-chat-content content={reasoningContent} role="assistant" />
);

// 是否选中深度思考
const isDeepSeekChecked = ref(false);
const deepseekCheckClick = () => {
  isDeepSeekChecked.value = !isDeepSeekChecked.value;
};

const base64Images = ref([]);
function handleImgChange(event) {
  const file = event.target.files[0];
  if (file) {
    const reader = new FileReader();
    reader.onload = (e) => {
      base64Images.value.push(e.target.result);
    };
    reader.readAsDataURL(file);
  }
}

const visible = ref(false);
const removeImage = (index) => {
  base64Images.value.splice(index, 1);
};
</script>
<style lang="less">
.chat-container {
  height: 900px !important;
  .chat-sender {
    .btn {
      color: var(--td-text-color-disabled);
      border: none;
      &:hover {
        color: var(--td-brand-color-hover);
        border: none;
        background: none;
      }
    }
    .btn.t-button {
      height: var(--td-comp-size-m);
      padding: 0;
    }
    .model-select {
      display: flex;
      align-items: center;
      .t-select {
        width: 180px;
        height: var(--td-comp-size-m);
        margin-right: var(--td-comp-margin-s);
        .t-input {
          border-radius: 32px;
          padding: 0 15px;
        }
        .t-input.t-is-focused {
          box-shadow: none;
        }
      }
      .check-box {
        width: 112px;
        height: var(--td-comp-size-m);
        border-radius: 32px;
        border: 0;
        background: var(--td-bg-color-component);
        color: var(--td-text-color-primary);
        box-sizing: border-box;
        flex: 0 0 auto;
        display: flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
        .t-icon {
          margin-right: var(--td-comp-margin-xs);
        }
        &:hover {
          border: 1px solid var(--td-brand-color-focus);
          background: var(--td-brand-color-light);
          color: var(--td-text-color-brand);
        }
        .t-button__text {
          display: flex;
          align-items: center;
          justify-content: center;
          span {
            margin-left: var(--td-comp-margin-xs);
          }
        }
      }
      .check-box.is-active {
        border: 1px solid var(--td-brand-color-focus);
        background: var(--td-brand-color-light);
        color: var(--td-text-color-brand);
      }
      .images-btn {
        width: 40px !important;
        margin-left: 10px;
        padding-right: 5px;
      }
    }
  }
}
.image-container {
  position: relative;
}
.image-wrapper {
  position: relative;
  display: inline-block;
}
.delete-btn {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(0, 0, 0, 0.5);
  color: white;
  z-index: 10;
}
</style>